import 'package:built_collection/built_collection.dart';
import 'package:mobile_app.core/core/domain/entities/dialect.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/collection_list_item/collection_list_item.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/lexical_item/lexical_item.dart';
import 'package:mobile_app.core/core/domain/entities/premium_access_status.dart';
import 'package:mobile_app.home.core/core/domain/helpers/get_permitted_collection_list_item_ids/helper.dart';
import 'package:mobile_app.home.core/core/domain/helpers/get_permitted_lexical_item_ids/helper.dart';
import 'package:mobile_app.home.core/core/domain/helpers/is_lexical_item_safe_to_pronounce/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_available_for_first_training_lexical_item_limit/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_learnable_learning_queue/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_learnable_unknown_lexical_items/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_learnable_unspecified_lexical_item_ids/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_repeatable_lexical_items/helper.dart';
import 'package:mobile_app.home.learning.core/core/domain/helpers/get_retrainable_lexical_items/helper.dart';
import 'package:mobile_app.home.learning.spaced_repetitions/spaced_repetition_page/domain/entities/card_infos/card_info.dart';
import 'package:mobile_app.home.learning.spaced_repetitions/spaced_repetition_page/domain/helpers/get_high_priority_lexical_item_ids/helper.dart';
import 'package:mobile_app.home.learning.spaced_repetitions/spaced_repetition_page/domain/helpers/pick_repeatable_lexical_item/helper.dart';
import 'package:mobile_app.home.learning.spaced_repetitions/spaced_repetition_page/domain/helpers/pick_unspecified_lexical_item/helper.dart';

abstract class GenerateCardInfo {
  CardInfo call({
    required BuiltMap<String, LexicalItem> lexicalItemMap,
    required BuiltList<CollectionListItem> collectionListItems,
    required PremiumAccessStatus premiumAccessStatus,
    required Dialect dialect,
    required BuiltMap<Dialect, BuiltSet<String>> unsafeToPronounceLexicalItemTitleMap,
    required int trainingSize,
    required bool repetitionTypingModeEnabled,
    required String? lastSkippedUnspecifiedLexicalItemId,
    required String? lastRepeatableLexicalItemId,
    required String? lastUnspecifiedLexicalItemId,
  });
}

class GenerateCardInfoImpl implements GenerateCardInfo {
  const GenerateCardInfoImpl({
    required GetAvailableForFirstTrainingLexicalItemLimit
        getAvailableForFirstTrainingLexicalItemLimit,
    required GetHighPriorityLexicalItemIds getHighPriorityLexicalItemIds,
    required GetLearnableLearningQueue getLearnableLearningQueue,
    required GetLearnableUnknownLexicalItems getLearnableUnknownLexicalItems,
    required GetLearnableUnspecifiedLexicalItemIds getLearnableUnspecifiedLexicalItemIds,
    required GetPermittedLexicalItemIds getPermittedLexicalItemIds,
    required GetPermittedCollectionListItemIds getPermittedCollectionListItemIds,
    required GetRepeatableLexicalItems getRepeatableLexicalItems,
    required GetRetrainableLexicalItems getRetrainableLexicalItems,
    required IsLexicalItemSafeToPronounce isLexicalItemSafeToPronounce,
    required PickRepeatableLexicalItem pickRepeatableLexicalItem,
    required PickUnspecifiedLexicalItem pickUnspecifiedLexicalItem,
  })  : _getAvailableForFirstTrainingLexicalItemLimit =
            getAvailableForFirstTrainingLexicalItemLimit,
        _getHighPriorityLexicalItemIds = getHighPriorityLexicalItemIds,
        _getLearnableLearningQueue = getLearnableLearningQueue,
        _getLearnableUnknownLexicalItems = getLearnableUnknownLexicalItems,
        _getLearnableUnspecifiedLexicalItemIds = getLearnableUnspecifiedLexicalItemIds,
        _getPermittedLexicalItemIds = getPermittedLexicalItemIds,
        _getPermittedCollectionListItemIds = getPermittedCollectionListItemIds,
        _getRepeatableLexicalItems = getRepeatableLexicalItems,
        _getRetrainableLexicalItems = getRetrainableLexicalItems,
        _isLexicalItemSafeToPronounce = isLexicalItemSafeToPronounce,
        _pickRepeatableLexicalItem = pickRepeatableLexicalItem,
        _pickUnspecifiedLexicalItem = pickUnspecifiedLexicalItem;

  final GetAvailableForFirstTrainingLexicalItemLimit _getAvailableForFirstTrainingLexicalItemLimit;
  final GetHighPriorityLexicalItemIds _getHighPriorityLexicalItemIds;
  final GetLearnableLearningQueue _getLearnableLearningQueue;
  final GetLearnableUnknownLexicalItems _getLearnableUnknownLexicalItems;
  final GetLearnableUnspecifiedLexicalItemIds _getLearnableUnspecifiedLexicalItemIds;
  final GetPermittedLexicalItemIds _getPermittedLexicalItemIds;
  final GetPermittedCollectionListItemIds _getPermittedCollectionListItemIds;
  final GetRepeatableLexicalItems _getRepeatableLexicalItems;
  final GetRetrainableLexicalItems _getRetrainableLexicalItems;
  final IsLexicalItemSafeToPronounce _isLexicalItemSafeToPronounce;
  final PickRepeatableLexicalItem _pickRepeatableLexicalItem;
  final PickUnspecifiedLexicalItem _pickUnspecifiedLexicalItem;

  @override
  CardInfo call({
    required BuiltMap<String, LexicalItem> lexicalItemMap,
    required BuiltList<CollectionListItem> collectionListItems,
    required PremiumAccessStatus premiumAccessStatus,
    required Dialect dialect,
    required BuiltMap<Dialect, BuiltSet<String>> unsafeToPronounceLexicalItemTitleMap,
    required int trainingSize,
    required bool repetitionTypingModeEnabled,
    required String? lastSkippedUnspecifiedLexicalItemId,
    required String? lastRepeatableLexicalItemId,
    required String? lastUnspecifiedLexicalItemId,
  }) {
    final repeatableLexicalItems = _getRepeatableLexicalItems(
      lexicalItemMap: lexicalItemMap,
    );

    final permittedCollectionListItemIds = _getPermittedCollectionListItemIds(
      collectionListItems: collectionListItems,
      premiumAccessStatus: premiumAccessStatus,
    );

    final permittedLexicalItemIds = _getPermittedLexicalItemIds(
      collectionListItems: collectionListItems,
      lexicalItemMap: lexicalItemMap,
      permittedCollectionListItemIds: permittedCollectionListItemIds,
      premiumAccessStatus: premiumAccessStatus,
    );

    final highPriorityLexicalItemIds = _getHighPriorityLexicalItemIds(
      permittedCollectionListItemIds: permittedCollectionListItemIds,
      collectionListItems: collectionListItems,
      lexicalItemMap: lexicalItemMap,
    );

    final repeatableLexicalItemPickingResult = _pickRepeatableLexicalItem(
      lexicalItemMap: lexicalItemMap,
      repeatableLexicalItems: repeatableLexicalItems,
      highPriorityLexicalItemIds: highPriorityLexicalItemIds,
      lastRepeatableLexicalItemId: lastRepeatableLexicalItemId,
    );

    if (repeatableLexicalItemPickingResult.repeatableLexicalItemCount > 0) {
      if (repetitionTypingModeEnabled) {
        return RepetitionAnswerTypingCardInfo(
          lexicalItem: repeatableLexicalItemPickingResult.lexicalItem!,
          repeatableLexicalItemCount: repeatableLexicalItemPickingResult.repeatableLexicalItemCount,
          hintUsed: false,
          finishEvent: RepetitionAnswerTypingCardFinishEvent.none,
        );
      } else {
        return RepetitionAnswerSelectionObscureCardInfo(
          lexicalItem: repeatableLexicalItemPickingResult.lexicalItem!,
          repeatableLexicalItemCount: repeatableLexicalItemPickingResult.repeatableLexicalItemCount,
          hintUsed: false,
          finishEvent: RepetitionAnswerSelectionObscureCardFinishEvent.none,
        );
      }
    }

    final retrainableLexicalItems = _getRetrainableLexicalItems(
      lexicalItemMap: lexicalItemMap,
    );

    final unknownLexicalItems = _getLearnableUnknownLexicalItems(
      lexicalItemMap: lexicalItemMap,
    );

    final learningQueue = _getLearnableLearningQueue(
      lexicalItemMap: lexicalItemMap,
    );

    final availableForFirstTrainingLexicalItemLimit = _getAvailableForFirstTrainingLexicalItemLimit(
      lexicalItemMap: lexicalItemMap,
      premiumAccessStatus: premiumAccessStatus,
    );

    if (availableForFirstTrainingLexicalItemLimit != null &&
        availableForFirstTrainingLexicalItemLimit < trainingSize - retrainableLexicalItems.length &&
        unknownLexicalItems.length + learningQueue.length >=
            availableForFirstTrainingLexicalItemLimit) {
      return DailyLimitCardInfo();
    }

    final unspecifiedLexicalItemIds = _getLearnableUnspecifiedLexicalItemIds(
      lexicalItemMap: lexicalItemMap,
      collectionListItems: collectionListItems,
      permittedCollectionListItemIds: permittedCollectionListItemIds,
      permittedLexicalItemIds: permittedLexicalItemIds,
    );

    final unspecifiedLexicalItem = _pickUnspecifiedLexicalItem(
      lexicalItemMap: lexicalItemMap,
      highPriorityLexicalItemIds: highPriorityLexicalItemIds,
      unspecifiedLexicalItemIds: unspecifiedLexicalItemIds,
      lastSkippedUnspecifiedLexicalItemId: lastSkippedUnspecifiedLexicalItemId,
      lastUnspecifiedLexicalItemId: lastUnspecifiedLexicalItemId,
    );

    if (unspecifiedLexicalItem != null) {
      final safeToPronounce = _isLexicalItemSafeToPronounce(
        lexicalItem: unspecifiedLexicalItem,
        dialect: dialect,
        unsafeToPronounceLexicalItemTitleMap: unsafeToPronounceLexicalItemTitleMap,
      );

      return UnspecifiedLexicalItemObscureCardInfo(
        lexicalItem: unspecifiedLexicalItem,
        safeToPronounce: safeToPronounce,
        finishEvent: UnspecifiedLexicalItemObscureCardFinishEvent.none,
      );
    }

    return LearningFinishCardInfo();
  }
}
